#include "StdAfx.h"
#include "Timer.h"
#include "Camera.h"
#include "Player.h"
#include "Scene.h"
#include "GameObject.h"

CScene::CScene(void)
{
    m_pD3DDevice = NULL;

	m_nMaterials = 0;
    m_nTextures = 0;

    m_nCurrentMaterial = -1;               
    m_nCurrentTexture = -1;                

	m_nLights = 0;

    //m_nObjects = 0;
    //m_pObjects = NULL;
	m_bEffect = false;
	D3DXMatrixIdentity(&EffectTextureTranslation);

	m_ControlPlayer = 1;

	m_pCamera = NULL;    
	//m_pPlayer = NULL;   

	m_pTerrain = NULL;
	m_pSkyBox = NULL;

	m_nRotatingObject = -1;
	m_nMovingObject = -1;

    ZeroMemory(m_ppd3dMaterials, MAX_MATERIALS * sizeof(D3DMATERIAL9 *));
    ZeroMemory(m_ppTextureItems, MAX_TEXTURES * sizeof(TEXTUREITEMSTRUCT *));
    ZeroMemory(m_ppd3dLights, MAX_LIGHTS * sizeof(D3DLIGHT9 *));

    ZeroMemory(&m_d3dDefaultMaterial, sizeof(D3DMATERIAL9));
    m_d3dDefaultMaterial.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
	m_pd3dDefaultTexture = NULL;
}

CScene::~CScene(void)
{
}

int CScene::CollectTexture(LPVOID pContext, LPTSTR pstrTextureFileName, int nMipMapLevels)
{    
    CScene *pScene = (CScene *)pContext;
    if (!pScene || !pstrTextureFileName) return(-1);

    int nMatchedTexture = -1;
    for (int t = 0; t < pScene->m_nTextures; t++)
    {
        if (pScene->m_ppTextureItems[t] && pScene->m_ppTextureItems[t]->m_pstrFileName) 
		{
			if (_tcsicmp(pstrTextureFileName, pScene->m_ppTextureItems[t]->m_pstrFileName) == 0) 
			{
				nMatchedTexture = t;
				break;
			}   
		}
    } 
    if (nMatchedTexture < 0)
    {
		HRESULT hResult;
		TEXTUREITEMSTRUCT *pNewTextureItem = new TEXTUREITEMSTRUCT;
		if (FAILED(hResult = D3DXCreateTextureFromFileEx(pScene->m_pD3DDevice, pstrTextureFileName, D3DX_DEFAULT, D3DX_DEFAULT, nMipMapLevels, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &pNewTextureItem->m_pd3dTexture))) return(-1);
		pNewTextureItem->m_pstrFileName = _tcsdup(pstrTextureFileName);
		pScene->m_ppTextureItems[pScene->m_nTextures] = pNewTextureItem;    
        nMatchedTexture = pScene->m_nTextures;
		pScene->m_nTextures++;
    }
    return(nMatchedTexture);
}

ATTRIBUTEITEMSTRUCT CScene::CollectAttributeData(LPVOID pContext, LPWSTR pstrTextureFileName, const D3DMATERIAL9 *pd3dMaterial)
{
    int nMatchedTexture = -1, nMatchedMaterial = -1;
	ATTRIBUTEITEMSTRUCT AttributeItem;
    
    CScene *pScene = (CScene *)pContext;
	if (pScene)
	{
		if (pd3dMaterial)
		{
			for (int m = 0; m < pScene->m_nMaterials; m++)
			{
				if (memcmp(pd3dMaterial, pScene->m_ppd3dMaterials[m], sizeof(D3DMATERIAL9)) == 0) 
				{
					nMatchedMaterial = m;
					break;   
				}
			}
		}
		if (pstrTextureFileName)
		{
			for (int t = 0; t < pScene->m_nTextures; t++)
			{
				if (pScene->m_ppTextureItems[t] && pScene->m_ppTextureItems[t]->m_pstrFileName) 
				{
					if (_tcsicmp(pstrTextureFileName, pScene->m_ppTextureItems[t]->m_pstrFileName) == 0) 
					{
						nMatchedTexture = t;
						break;
					}   
				}
			} 
		} 
		if ((nMatchedMaterial < 0) && pd3dMaterial)
		{
			pScene->m_ppd3dMaterials[pScene->m_nMaterials] = new D3DMATERIAL9;
			*pScene->m_ppd3dMaterials[pScene->m_nMaterials] = *pd3dMaterial;    
			nMatchedMaterial = pScene->m_nMaterials;
			pScene->m_nMaterials++;
		} 
		AttributeItem.m_nMaterial = -1;
		AttributeItem.m_nTexture = -1;		
		if ((nMatchedTexture < 0) && pstrTextureFileName)
		{
			HRESULT hResult;
			TEXTUREITEMSTRUCT *pNewTextureItem = new TEXTUREITEMSTRUCT;
			if (FAILED(hResult = D3DXCreateTextureFromFileEx(pScene->m_pD3DDevice, pstrTextureFileName, D3DX_DEFAULT, D3DX_DEFAULT, 0, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &pNewTextureItem->m_pd3dTexture))) return(AttributeItem);
			pNewTextureItem->m_pstrFileName = _tcsdup(pstrTextureFileName);
			pScene->m_ppTextureItems[pScene->m_nTextures] = pNewTextureItem;    
			nMatchedTexture = pScene->m_nTextures;
			pScene->m_nTextures++;
		}
	}
    AttributeItem.m_nTexture = nMatchedTexture;
    AttributeItem.m_nMaterial = nMatchedMaterial;

    return(AttributeItem);
}

int CScene::AddMaterial(D3DMATERIAL9 *pd3dMaterial) 
{ 
    int nMatchedMaterial = -1;
    if (pd3dMaterial)
    {
        for (int m = 0; m < m_nMaterials; m++)
        {
            if (memcmp(pd3dMaterial, m_ppd3dMaterials[m], sizeof(D3DMATERIAL9)) == 0) 
			{
				nMatchedMaterial = m;
				break;   
			}
        }
    }
	if (nMatchedMaterial < 0) 
	{
		m_ppd3dMaterials[m_nMaterials] = pd3dMaterial; 
		nMatchedMaterial = m_nMaterials;
		m_nMaterials++;
	}
	return(nMatchedMaterial); 
}

void CScene::BuildObjects()
{
	//m_nObjects = 0;
	//m_pObjects = new CGameObject[m_nObjects];

	//CMesh *pCubeMesh = new CCubeMesh();
 //   if (pCubeMesh) 
	//{
	//	pCubeMesh->Build(m_pD3DDevice, this, D3DXMESH_MANAGED);
	//	m_pObjects[0].SetMesh(pCubeMesh);
	//	D3DXMatrixTranslation(&m_pObjects[0].m_mtxWorld, -300.5f, -2.0f, 14.0f);
	//}
	//CMesh *pSpaceShipMesh = new CSpaceShipMesh();
 //   if (pSpaceShipMesh) 
	//{
	//	pSpaceShipMesh->Build(m_pD3DDevice, this, D3DXMESH_MANAGED);
	//	m_pObjects[1].SetMesh(pSpaceShipMesh);
	//	D3DXMatrixTranslation(&m_pObjects[1].m_mtxWorld, 500.5f, -200.0f, 100.0f);
	//}
	//CMesh *pCruiser01Mesh = new CCruiser01Mesh();
 //   if (pCruiser01Mesh) 
	//{
	//	pCruiser01Mesh->Build(m_pD3DDevice, this, D3DXMESH_MANAGED);
	//	m_pObjects[2].SetMesh(pCruiser01Mesh);
	//	D3DXMatrixTranslation(&m_pObjects[2].m_mtxWorld, -350.5f, 800.0f, 34.0f);
	//}
	//CMesh *pCruiser02Mesh = new CCruiser02Mesh();
 //   if (pCruiser02Mesh) 
	//{
	//	pCruiser02Mesh->Build(m_pD3DDevice, this, D3DXMESH_MANAGED);
	//	m_pObjects[3].SetMesh(pCruiser02Mesh);
	//	D3DXMatrixTranslation(&m_pObjects[3].m_mtxWorld, 350.5f, -400.0f, 240.0f);
	//}
	//CMesh *pAsteroidMesh = new CAsteroidMesh();
 //   if (pAsteroidMesh) 
	//{
	//	pAsteroidMesh->Build(m_pD3DDevice, this, D3DXMESH_MANAGED);
	//	m_pObjects[4].SetMesh(pAsteroidMesh);
	//	D3DXMatrixTranslation(&m_pObjects[4].m_mtxWorld, 200.0f, 65.0f, 1505.0f);
	//}

	m_pSkyBox = new CSkyBox();
	if (m_pSkyBox) m_pSkyBox->Build(m_pD3DDevice, this, D3DXMESH_MANAGED);

	m_nLights = 4;
	for (int i = 0; i < m_nLights; i++) 
	{
		m_ppd3dLights[i] = new D3DLIGHT9;
		ZeroMemory(m_ppd3dLights[i], sizeof(D3DLIGHT9));
	}
    m_ppd3dLights[0]->Type = D3DLIGHT_DIRECTIONAL;
    m_ppd3dLights[0]->Diffuse = D3DXCOLOR(1.0f, 0.92f, 0.82f, 0.0f);
    m_ppd3dLights[0]->Specular = D3DXCOLOR(0.33f, 0.30f, 0.27f, 0.0f);
    m_ppd3dLights[0]->Direction = D3DXVECTOR3(0.819f, -0.573f, 0.0f);
    m_ppd3dLights[1]->Type = D3DLIGHT_DIRECTIONAL;
    m_ppd3dLights[1]->Diffuse = D3DXCOLOR(0.4f, 0.4f, 0.4f, 0.0f);
    m_ppd3dLights[1]->Specular = D3DXCOLOR(0.2f, 0.2f, 0.2f, 0.0f);
    m_ppd3dLights[1]->Direction = D3DXVECTOR3(-0.819f, -0.573f, -0.0f);
    m_ppd3dLights[2]->Type = D3DLIGHT_DIRECTIONAL;
    m_ppd3dLights[2]->Diffuse = D3DXCOLOR(0.8f, 0.8f, 0.8f, 0.0f);
    m_ppd3dLights[2]->Specular = D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f);
    m_ppd3dLights[2]->Direction = D3DXVECTOR3(0.0f, 0.707107f, -0.707107f);
    m_ppd3dLights[3]->Type = D3DLIGHT_DIRECTIONAL;
    m_ppd3dLights[3]->Diffuse = D3DXCOLOR(0.6f, 0.6f, 0.6f, 0.0f);
    m_ppd3dLights[3]->Specular = D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f);
    m_ppd3dLights[3]->Direction = D3DXVECTOR3(0.0f, 0.707107f, 0.707107f);
	for (int i = 0; i < m_nLights; i++)
	{
        m_pD3DDevice->SetLight(i, m_ppd3dLights[i]);
        m_pD3DDevice->LightEnable(i, TRUE);
	}

	m_pTerrain = new CTerrain();
	if (m_pTerrain) m_pTerrain->Build(this, m_pD3DDevice, D3DUSAGE_WRITEONLY, D3DPOOL_MANAGED);

	// Add Player1
	m_plPlayer.push_back(new CPlayer());
	CMesh *pPlayerMesh = new CMesh();
	//pPlayerMesh->Build(m_pD3DDevice, this);
	/*pPlayerMesh->LoadFromXFile("Data/Car.x", D3DXMESH_MANAGED, m_pD3DDevice, this); */
	pPlayerMesh->LoadFromXFile(_T("Data/sfb-04.X"), D3DXMESH_MANAGED, m_pD3DDevice, this); 
	CGameObject *p3rdPersonObject = new CGameObject();
	p3rdPersonObject->SetMesh(pPlayerMesh);
	m_plPlayer[m_plPlayer.size()-1]->Set3rdPersonObject(p3rdPersonObject);
	BOUNDINGCUBE BoundCube = pPlayerMesh->m_bcBoundingCube;
	m_plPlayer[m_plPlayer.size()-1]->SetBoundingCube(BoundCube);
	m_plPlayer[m_plPlayer.size()-1]->SetCameraMode(THIRD_PERSON_CAMERA);

	//D3DXMATRIX mat;
	//D3DXMatrixRotationY(&mat, D3DXToRadian(-90) );
	//m_pPlayer->SetBaseRotation(mat);
	m_plPlayer[m_plPlayer.size()-1]->SetFloat(100.0f);

	m_plPlayer[m_plPlayer.size()-1]->AddPlayerCallback((LPUPDATEPLAYERCALLBACK)CTerrain::UpdatePlayerCallback, (LPVOID)m_pTerrain);
	m_plPlayer[m_plPlayer.size()-1]->AddCameraCallback((LPUPDATECAMERACALLBACK)CTerrain::UpdateCameraCallback, (LPVOID)m_pTerrain);


	CMesh*	EffectMesh = new CMesh();
	EffectMesh->LoadFromXFile(_T("Data/Effect.x"), D3DXMESH_MANAGED, m_pD3DDevice, this); 
	m_pEffect[0] = new CGameObject();
	m_pEffect[0]->SetMesh(EffectMesh);
	m_pEffect[1] = new CGameObject();
	m_pEffect[1]->SetMesh(EffectMesh);
	m_pEffect[2] = new CGameObject();
	m_pEffect[2]->SetMesh(EffectMesh);
	m_pEffect[3] = new CGameObject();
	m_pEffect[3]->SetMesh(EffectMesh);

	D3DXMatrixTranslation(&EffectTranslation[0], -26.0f, 23.0f, -130.0f);
	D3DXMatrixTranslation(&EffectTranslation[1],  26.0f, 23.0f, -130.0f);
	D3DXMatrixTranslation(&EffectTranslation[2], -21.0f, -14.0f, -130.0f);
	D3DXMatrixTranslation(&EffectTranslation[3],  21.0f, -14.0f, -130.0f);
	
}

void CScene::ReleaseObjects()
{
	//if (m_pObjects) delete [] m_pObjects;
	//m_pObjects = NULL;
	m_lObjects.clear();

	for(int i = 0; i < m_plPlayer.size(); ++i) 
		delete m_plPlayer[i];
	m_plPlayer.clear();
	

	if (m_pTerrain) delete m_pTerrain;
	m_pTerrain = NULL;

	if (m_pSkyBox) delete m_pSkyBox;

	for(int effect = 0; effect < 4; ++effect)
		delete m_pEffect[effect];	

	for (int i = 0; i < m_nMaterials; i++) if (m_ppd3dMaterials[i]) delete m_ppd3dMaterials[i];
	for (int i = 0; i < m_nTextures; i++) 
	{ 
		if (m_ppTextureItems[i]) 
		{
			if (m_ppTextureItems[i]->m_pstrFileName) free(m_ppTextureItems[i]->m_pstrFileName); 
			if (m_ppTextureItems[i]->m_pd3dTexture) m_ppTextureItems[i]->m_pd3dTexture->Release();
			delete m_ppTextureItems[i];
		}
	}
	for (int i = 0; i < m_nLights; i++) if (m_ppd3dLights[i]) delete m_ppd3dLights[i];

	if (m_pd3dDefaultTexture) m_pd3dDefaultTexture->Release();

	if (m_pD3DDevice) m_pD3DDevice->Release();
}

void CScene::OnChangeCamera(ULONG nCameraMode, float fTimeDistance)
{
    switch (nCameraMode)
    {
        case THIRD_PERSON_CAMERA:
			m_ControlPlayer = 0;
            m_plPlayer[0]->SetFriction(250.0f); 
            m_plPlayer[0]->SetGravity(D3DXVECTOR3(0.0f, -400.0f, 0.0f));
            m_plPlayer[0]->SetMaxVelocityXZ(400.0f);
            m_plPlayer[0]->SetMaxVelocityY(400.0f);
            m_plPlayer[0]->SetCameraOffset(D3DXVECTOR3(0.0f, 200.0f, -350.0f));
            m_plPlayer[0]->SetCameraLag(0.25f); 
            //m_plPlayer[0]->SetCameraMode(THIRD_PERSON_CAMERA);
            m_pCamera = m_plPlayer[0]->GetCamera();
            break;

		default:
			break;
	}
	m_plPlayer[m_ControlPlayer]->Update(fTimeDistance);
	m_pCamera->SetViewMatrixToDevice(m_pD3DDevice);
	//if (m_pCamera && m_pScene) m_pScene->SetCamera(m_pCamera);
}

int CScene::PickObjectPointedByCursor(int xClient, int yClient)
{
	if (!m_pCamera || m_lObjects.empty()) return(-1);

	D3DXMATRIX mtxProjection = m_pCamera->GetProjectionMatrix();
    D3DVIEWPORT9 vpViewport = m_pCamera->GetViewport();
	D3DXMATRIX mtxView = m_pCamera->GetViewMatrix();
    D3DXVECTOR3 vPickPosition;
	vPickPosition.x = (((2.0f * xClient) / vpViewport.Width) - 1) / mtxProjection._11;
	vPickPosition.y = -(((2.0f * yClient) / vpViewport.Height) - 1) / mtxProjection._22;
    vPickPosition.z = 1.0f;

	BOOL bIntersected = FALSE;
	float fNearHitDistance = FLT_MAX, fHitDistance;
	int nSelectedObject = -1;
	for (int i = 0; i < m_lObjects.size(); i++) 
	{
		bIntersected = m_lObjects[i]->m_pMesh->CheckRayIntersection(m_pD3DDevice, &m_lObjects[i]->m_mtxWorld, &mtxView, &vPickPosition, &fHitDistance);
		if (bIntersected && (fHitDistance < fNearHitDistance)) 
		{
			fNearHitDistance = fHitDistance;
			nSelectedObject = i;
		}
	}

	return(nSelectedObject);
}

void CScene::AnimateObjects(float fElapsedTime)
{	
	//if (m_pObjects)	m_pObjects[0].Rotate(20.0f * fElapsedTime, 30.0f * fElapsedTime, 15.0f * fElapsedTime);
	//m_pPlayer->Update(fElapsedTime);
	m_plPlayer[0]->Update(fElapsedTime);	
	m_pCamera->SetViewMatrixToDevice(m_pD3DDevice);


	for(int effect = 0; effect < 4; ++effect) {
		D3DXMATRIX EffectScale;
		//if(effect % 2)
		//	D3DXMatrixRotationZ(&EffectRotation, D3DXToRadian(EffectRadian));
		//else
		//	D3DXMatrixRotationZ(&EffectRotation, D3DXToRadian(-EffectRadian));
		D3DXMatrixScaling(&EffectScale, 1.0f, 1.0f, 1.0f + (rand() % 5) / 10.0f);
	 	
		m_pEffect[effect]->m_mtxWorld = m_plPlayer[0]->GetTransform();
		m_pEffect[effect]->m_mtxWorld = EffectScale * EffectTranslation[effect] * m_pEffect[effect]->m_mtxWorld;
	}


	EffectTextureTranslation._32 += 2.0f * fElapsedTime;
	if(EffectTextureTranslation._32 > 1.0f)
		EffectTextureTranslation._32 -= 1.0f;	

	
	
}

void CScene::MoveObjects(int nMovingObject, float x, float y, float z)
{
	if (nMovingObject < 0) nMovingObject = m_nMovingObject;
	if ((nMovingObject >= 0) && !m_lObjects.empty())
	{
		if (x != 0.0f) m_lObjects[nMovingObject]->MoveStrafe(x);
		if (y != 0.0f) m_lObjects[nMovingObject]->MoveUpDown(y);
		if (z != 0.0f) m_lObjects[nMovingObject]->MoveForward(z);
	}
}

void CScene::RotateObjects(int nRotatingObject, float fPitch, float fYaw, float fRoll)
{
	if (nRotatingObject < 0) nRotatingObject = m_nRotatingObject;
	if ((nRotatingObject >= 0) && !m_lObjects.empty())
	{
		m_lObjects[nRotatingObject]->Rotate(fPitch, fYaw, fRoll);
	}
}

void CScene::RenderObjects()
{
/*	for (int i = 0; i < m_nObjects; i++) 
	{
		m_pObjects[i].Render(m_pD3DDevice, m_pCamera, this);
	}*/ 
}

void CScene::Render()
{	
	if (m_pSkyBox) m_pSkyBox->Render(m_pD3DDevice, m_pCamera, this);	
	
	if (m_pTerrain) m_pTerrain->Render(m_pD3DDevice, this);	
	for(int i = 0; i < m_plPlayer.size(); ++i)
		m_plPlayer[i]->Render(m_pD3DDevice, m_pCamera, this);	

	if(m_ControlPlayer == 0 && m_bEffect) {
		m_pD3DDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);		
		m_pD3DDevice->SetTransform(D3DTS_TEXTURE0, &EffectTextureTranslation);
		for(int i = 0; i < 4; ++i)
			m_pEffect[i]->Render(m_pD3DDevice, m_pCamera, this);
		m_pD3DDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
	}
	

	RenderObjects();
}

void CScene::OnEffect(void) {
	m_bEffect = true;
}

void CScene::OffEffect(void) {
	m_bEffect = false;
}
